﻿using System;
using ZyGame.Transport;
using ZyGame.Transport.Common;

namespace ZyGame.Game
{
    public class NetworkOptions : IDisposable
    {
        public enum NetworkType
        {
            TCP,
            KCP,
            WEB,
            None,
        }

        public NetworkType type;
        public string address;
        public ushort port;
        public string name;
        public int cid;

        public NetworkOptions(string name, NetworkType type = NetworkType.None, string address = "", ushort port = 0, int cid = 0)
        {
            this.name = name;
            this.type = type;
            this.address = address;
            this.port = port;
            this.cid = cid;
        }

        public void Dispose()
        {
        }
    }

    public class NetworkComponent<T> : EntityComponent where T : INetworkSerialized
    {
        class MessageHandle : IChannelHandler
        {
            NetworkComponent<T> networkComponent;

            public MessageHandle(NetworkComponent<T> network)
            {
                this.networkComponent = network;
            }

            public void Dispose()
            {
                networkComponent = null;
            }

            public void HandleActived(IChannel channel)
            {
                this.networkComponent.HandleActived();
            }

            public void HandleException(IChannel channel, Exception ex)
            {
                this.networkComponent.HandleException(ex);
            }

            public void HandleInactived(IChannel channel)
            {
                this.networkComponent.HandleInactived();
            }

            public void HandleReceive(IChannel channel, object message)
            {
                this.networkComponent.HandleReceive(message);
            }
        }

        private IChannel channel;
        private MessageHandle messageHandle;
        public NetworkOptions.NetworkType type { get; private set; }
        public string address { get; private set; }
        public ushort port { get; private set; }
        public string name { get; private set; }
        public int cid { get; private set; }

        public override async void Awake(params object[] dataList)
        {
            if (dataList is null || dataList.Length == 0)
            {
                return;
            }

            NetworkOptions options = dataList.GetAndCast<NetworkOptions>();
            if (options == null)
            {
                Client.Console.WriteError("not set network options");
                return;
            }

            this.type = options.type;
            this.address = options.address;
            this.port = options.port;
            this.cid = options.cid;
            this.name = options.name;

            channel = Client.Network.GetChannel(name);
            if (channel is null)
            {
                switch (type)
                {
                    case NetworkOptions.NetworkType.TCP:
                        channel = await Client.Network.OnConnectTcpSocket<T>(name, address, port);
                        break;
                    case NetworkOptions.NetworkType.KCP:
                        channel = await Client.Network.OnConnectKcpSocket<T>(name, cid, address, port);
                        break;
                    case NetworkOptions.NetworkType.WEB:
                        channel = await Client.Network.OnConnectWebSocket<T>(name, address);
                        break;
                }
            }

            messageHandle = new MessageHandle(this);
            channel.AddOutput(messageHandle);
        }

        public override void Dispose()
        {
            entity = null;
            channel.RemoveOutput(messageHandle);
            messageHandle = null;
            channel = null;
        }

        public async void WriteAndFlush(object message)
        {
            if (channel is null)
            {
                return;
            }

            await channel.WriteAndFlush(message);
        }

        protected virtual void HandleActived()
        {
        }

        protected virtual void HandleException(Exception ex)
        {
            Client.Console.WriteError(ex);
        }

        protected virtual void HandleInactived()
        {
        }

        protected virtual void HandleReceive(object message)
        {
        }
    }
}